/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "feature.h"
#include "config.h"
#include "debug.h"

static inline int file_exists(const char *file_name);
static inline const char * __trim_begin(const char *src);
static inline int __is_comment(const char *src);



/**
 * @brief Try to get the configure file
 * @return return NULL is not found
 */
FILE *config_file_get() {
    int i = 0;
    FILE *f;

    /* Search ~/.mokoko.conf first */
    char home_conf[1024];
    snprintf(home_conf,sizeof(home_conf),"%s/.mokoko.conf",getenv("HOME"));
    if(file_exists(home_conf)) {
            debug("Read config file from HOME directory\n");
            return fopen(home_conf,"r");
    }

    const char * candidate_list[] = {
        "/etc/mokoko.conf",
#ifdef CONFIG_PATH
        CONFIG_PATH"/mokoko.conf",
#endif
        NULL
    };
    for (i = 0;candidate_list[i];i++) {
        if (file_exists(candidate_list[i]))
            break;
    }
    if (candidate_list[i]) {
        debug("config file is %s\n", candidate_list[i]);
        f = fopen(candidate_list[i], "r");
        return f;
    }
    return NULL;
}

/** read a single line from a file, stopping at a newline or EOF.
 *  If a newline is read, it will appear in the resulting string.
 *  Return value is a malloc'ed char * which should be freed at
 *  some point by the caller.
 *
 *  @return value is NULL if the file is at EOF when called.
 */
#define FILE_READ_LINE_BUF_SIZE 1024
char *file_line_get(FILE *file) {
    char buf[FILE_READ_LINE_BUF_SIZE];
    int buf_len;
    char *line = NULL;
    int line_size = 0;

    memset(buf, 0, FILE_READ_LINE_BUF_SIZE);
    while (fgets(buf, FILE_READ_LINE_BUF_SIZE, file)) {
        buf_len = strlen(buf);
        if (line) {
            line_size += buf_len;
            line = realloc(line, line_size);
            if (line == NULL) {
                error("out of memory\n");
                break;
            }
            strcat(line, buf);
        } else {
            line_size = buf_len + 1;
            line = strdup(buf);
        }
        if (buf[buf_len - 1] == '\n') {
            break;
        }
    }
    if (line && strlen(line) && line[strlen(line)-1] == '\n') {
        line[strlen(line)-1] = '\0';
    }
    return line;
}


char * feature_get(const char *line) {
    char *ptr;
    size_t offset;
    if (!line)
        return NULL;
    line = __trim_begin(line);
    if (__is_comment(line))  // comments
        return NULL;
    ptr = index (line, '=');
    if (!ptr) {
        debug("There are no Feature set!!\n");
        return NULL;
    }
    if (ptr == line)
        return NULL;
    while (*(ptr - 1) == ' ' || *(ptr - 1) == '\t')
        ptr--;

    offset = (size_t)(ptr - line);
    ptr = strndup((const char *)line, (size_t)offset);
    return ptr;
}


/**
 * @brief Get tokens from first '"' to last '"' with sep ';'
 * @param src
 * @param which 0 means total message
 *        1 means the first token
 * @return NULL if no such token else returns the address of token clone
 */
char * token_get(const char *src, int which) {
    int i;
    char *start, *end, *pin;
    if (!src)
        return NULL;
    src = __trim_begin (src);
    if (__is_comment(src))
        return NULL;
    if (which < 0)
        return NULL;
    start = index(src, '"');
    end = rindex(src, '"');
    pin = start;
    if (start == NULL || start == end) {
        return NULL;
    }
    if (which == 0) {
        start ++;
        return strndup (start, (size_t)(end - start));
    }
    for (i = 0;i < which;i++) {
        pin = index(start, ';');
        if (i > 0) {
            if (pin == NULL)
                return NULL;
            start = pin;
        }
        start++;
        pin = index(start, ';');
        if (start > end)
            return NULL;
        if (pin == NULL || pin > end)
            pin = end;
    }
    return strndup (start, (size_t)(pin - start));
}

/*
 * @brief Read the default configurations
 */
void config_read() {
    FILE *f = config_file_get();
    char *line = NULL;
    char *feature = NULL;
    char *token = NULL;
    int i, ret;
    if (!f) {
        debug("Cannot find config file!!\n");
        return;
    }
    do {
        free(line);
        line = file_line_get(f);
        feature = feature_get(line);
        if (feature) {
            for (i = 1;;i++) {
                token = token_get(line, i);
                ret = mokoko_feature_value_add(NULL, feature, token);
                if (!ret) {
                    error("Adding Feature '%s' Value '%s failed!!\n'", feature, token);
                }
                if (!token)
                    break;
                free(token);
            }
        }
        free(feature);
    } while (line);
    fclose(f);
}

// ================
static inline int file_exists(const char *file_name) {
    int err;
    struct stat stat_buf;

    err = stat(file_name, &stat_buf);
    if (err == 0) {
        return 1;
    } else {
        return 0;
    }
}

static inline const char * __trim_begin(const char *src) {
    while (*src == ' ' || *src == '\t')
        src++;
    return src;
}

static inline int __is_comment(const char *src) {
    const char *start = __trim_begin(src);
    if (*start == '#')
        return 1;
    return 0;
}

